package com.dqx.opengl_es_demo.filter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;

import com.dqx.opengl_es_demo.R;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * 渲染器
 */
public class FilterRenderer implements GLSurfaceView.Renderer {
    //顶点着色器代码
    private String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "attribute vec2 vCoordinate;" +
                    "uniform mat4 vMatrix;" +
                    "varying vec2 aCoordinate;" +
                    "varying vec4 aPos;" +
                    "varying vec4 gPosition;" +
                    "void main(){" +
                    "    gl_Position=vMatrix*vPosition;" +
                    "    aPos=vPosition;" +
                    "    aCoordinate=vCoordinate;" +
                    "    gPosition=vMatrix*vPosition;" +
                    "}";
    //片元着色器代码
    private String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform sampler2D vTexture;" +
                    "uniform int vChangeType;" +
                    "uniform vec3 vChangeColor;" +
                    "uniform int vIsHalf;" +
                    "uniform float uXY;" +
                    "varying vec4 gPosition;" +
                    "varying vec2 aCoordinate;" +
                    "varying vec4 aPos;" +
                    "void modifyColor(vec4 color){" +
                    "    color.r=max(min(color.r,1.0),0.0);" +
                    "    color.g=max(min(color.g,1.0),0.0);" +
                    "    color.b=max(min(color.b,1.0),0.0);" +
                    "    color.a=max(min(color.a,1.0),0.0);" +
                    "}" +
                    "" +
                    "void main(){" +
                    "    vec4 nColor=texture2D(vTexture,aCoordinate);" +
                    "    if(aPos.x>0.0||vIsHalf==0){" +
                    "        if(vChangeType==1){" +
                    "            float c=nColor.r*vChangeColor.r+nColor.g*vChangeColor.g+nColor.b*vChangeColor.b;" +
                    "            gl_FragColor=vec4(c,c,c,nColor.a);" +
                    "        }else if(vChangeType==2){" +
                    "            vec4 deltaColor=nColor+vec4(vChangeColor,0.0);" +
                    "            modifyColor(deltaColor);" +
                    "            gl_FragColor=deltaColor;" +
                    "        }else if(vChangeType==3){" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x-vChangeColor.r,aCoordinate.y-vChangeColor.r));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x-vChangeColor.r,aCoordinate.y+vChangeColor.r));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x+vChangeColor.r,aCoordinate.y-vChangeColor.r));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x+vChangeColor.r,aCoordinate.y+vChangeColor.r));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x-vChangeColor.g,aCoordinate.y-vChangeColor.g));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x-vChangeColor.g,aCoordinate.y+vChangeColor.g));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x+vChangeColor.g,aCoordinate.y-vChangeColor.g));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x+vChangeColor.g,aCoordinate.y+vChangeColor.g));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x-vChangeColor.b,aCoordinate.y-vChangeColor.b));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x-vChangeColor.b,aCoordinate.y+vChangeColor.b));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x+vChangeColor.b,aCoordinate.y-vChangeColor.b));" +
                    "            nColor+=texture2D(vTexture,vec2(aCoordinate.x+vChangeColor.b,aCoordinate.y+vChangeColor.b));" +
                    "            nColor/=13.0;" +
                    "            gl_FragColor=nColor;" +
                    "        }else if(vChangeType==4){" +
                    "            float dis=distance(vec2(gPosition.x,gPosition.y/uXY),vec2(vChangeColor.r,vChangeColor.g));" +
                    "            if(dis<vChangeColor.b){" +
                    "                nColor=texture2D(vTexture,vec2(aCoordinate.x/2.0+0.25,aCoordinate.y/2.0+0.25));" +
                    "            }" +
                    "            gl_FragColor=nColor;" +
                    "        }else{" +
                    "            gl_FragColor=nColor;" +
                    "        }" +
                    "    }else{" +
                    "        gl_FragColor=nColor;" +
                    "    }" +
                    "}";
    //程序id
    private int mProgram;
    //位置属性句柄
    private int glHPosition;
    //纹理属性句柄
    private int glHTexture;
    //纹理坐标属性句柄
    private int glHCoordinate;
    //变换矩阵句柄
    private int glHMatrix;
    //处理类型句柄
    private int glHangeType;
    //颜色通道
    private int glHChangeColor;

    //位图
    private Bitmap mBitmap;
    //纹理对象id
    private int textureId;
    //相机矩阵
    private float[] mViewMatrix = new float[16];
    //投影矩阵
    private float[] mProjectMatrix = new float[16];
    //变换矩阵
    private float[] mMVPMatrix = new float[16];
    //顶点坐标缓冲区
    private FloatBuffer bPos;
    //纹理坐标缓冲区
    private FloatBuffer bCoord;
    //顶点坐标
    private final float[] sPos = {
            -1.0f, 1.0f,
            -1.0f, -1.0f,
            1.0f, 1.0f,
            1.0f, -1.0f
    };
    //纹理坐标
    private final float[] sCoord = {
            0.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 0.0f,
            1.0f, 1.0f,
    };
    //顶点着色器
    int vertexShader;
    //创建片元着色器
    int fragmentShader;

    public FilterRenderer(Context context) {
        mBitmap = BitmapFactory.decodeResource(context.getResources(),
                R.mipmap.image);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //设置背景（RGBA）
        GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        //初始化缓冲区对象
        bPos = initFloatBuffer(sPos);
        bCoord = initFloatBuffer(sCoord);
        //创建程序
        mProgram = creatProgram();

        //获取属性句柄
        glHPosition = GLES20.glGetAttribLocation(mProgram, "vPosition");
        glHCoordinate = GLES20.glGetAttribLocation(mProgram, "vCoordinate");
        glHTexture = GLES20.glGetUniformLocation(mProgram, "vTexture");
        glHMatrix = GLES20.glGetUniformLocation(mProgram, "vMatrix");
        glHangeType = GLES20.glGetUniformLocation(mProgram, "vChangeType");
        glHChangeColor = GLES20.glGetUniformLocation(mProgram, "vChangeColor");
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置视图窗口位置和大小
        GLES20.glViewport(0, 0, width, height);

        //设置投影矩阵
        int w = mBitmap.getWidth();
        int h = mBitmap.getHeight();
        float sWH = w / (float) h;
        float sWidthHeight = width / (float) height;
        if (width > height) {
            if (sWH > sWidthHeight) {
                Matrix.orthoM(mProjectMatrix, 0, -sWidthHeight * sWH, sWidthHeight * sWH, -1, 1, 3, 5);
            } else {
                Matrix.orthoM(mProjectMatrix, 0, -sWidthHeight / sWH, sWidthHeight / sWH, -1, 1, 3, 5);
            }
        } else {
            if (sWH > sWidthHeight) {
                Matrix.orthoM(mProjectMatrix, 0, -1, 1, -1 / sWidthHeight * sWH, 1 / sWidthHeight * sWH, 3, 5);
            } else {
                Matrix.orthoM(mProjectMatrix, 0, -1, 1, -sWH / sWidthHeight, sWH / sWidthHeight, 3, 5);
            }
        }
        //设置相机矩阵
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 5.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //清除深度缓存和颜色缓存
        GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

        //绘制
        draw();
    }

    /**
     * 绘制
     */
    public void draw() {
        Filter filter = Filter.GRAY;
        //使用程序
        GLES20.glUseProgram(mProgram);
        GLES20.glUniform1i(glHangeType, filter.getType());
        GLES20.glUniform3fv(glHChangeColor, 1, filter.data(), 0);
        //设置变换矩阵的值
        GLES20.glUniformMatrix4fv(glHMatrix, 1, false, mMVPMatrix, 0);
        //启用顶点位置属性句柄
        GLES20.glEnableVertexAttribArray(glHPosition);
        //启用纹理坐标属性句柄
        GLES20.glEnableVertexAttribArray(glHCoordinate);
        //清空纹理属性句柄的值
        GLES20.glUniform1i(glHTexture, 0);
        //创建纹理贴图
        textureId = createTexture();
        //设置顶点位置的值
        GLES20.glVertexAttribPointer(glHPosition, 2, GLES20.GL_FLOAT, false, 0, bPos);
        //设置纹理坐标的值
        GLES20.glVertexAttribPointer(glHCoordinate, 2, GLES20.GL_FLOAT, false, 0, bCoord);
        //绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }

    /**
     * 初始化缓冲区对象
     *
     * @param buffer
     * @return
     */
    private FloatBuffer initFloatBuffer(float[] buffer) {
        FloatBuffer fb = ByteBuffer.allocateDirect(buffer.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(buffer);
        fb.position(0);
        return fb;
    }

    /**
     * 创建着色器，并返回着色器对象id
     *
     * @param type       着色器类型
     * @param shaderCode 着色器代码
     * @return
     */
    private int loadShader(int type, String shaderCode) {
        //创建着色器对象
        int shader = GLES20.glCreateShader(type);
        //加载着色器代码源
        GLES20.glShaderSource(shader, shaderCode);
        //编译着色器
        GLES20.glCompileShader(shader);
        return shader;
    }

    /**
     * 创建程序
     */
    private int creatProgram() {
        //程序对象id
        int program = 0;
        //创建顶点着色器
        vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        //创建片元着色器
        fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
        //创建程序对象
        program = GLES20.glCreateProgram();
        //将顶点着色器加到程序中
        GLES20.glAttachShader(program, vertexShader);
        //将片段着色器加到程序中
        GLES20.glAttachShader(program, fragmentShader);
        //连接着色器
        GLES20.glLinkProgram(program);
        return program;
    }

    /**
     * 创建纹理贴图
     *
     * @return
     */
    private int createTexture() {
        int[] texture = new int[1];
        if (mBitmap != null && !mBitmap.isRecycled()) {
            //生成纹理
            GLES20.glGenTextures(1, texture, 0);
            //生成纹理
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
            //设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
            //设置放大过滤为使用纹理中坐标最接近的若干个颜色，通过加权平均算法得到需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
            //设置环绕方向S，截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
            //设置环绕方向T，截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
            //根据以上指定的参数，生成一个2D纹理
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);
            return texture[0];
        }
        return 0;
    }

}
